package com.nbcio.pm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.nbcio.common.core.utils.MapstructUtils;
import com.nbcio.common.core.utils.StringUtils;
import com.nbcio.common.core.utils.file.FileUploadUtils;
import com.nbcio.common.core.utils.file.MimeTypeUtils;
import com.nbcio.common.oss.core.OssClient;
import com.nbcio.common.oss.factory.OssFactory;
import com.nbcio.common.oss.entity.UploadResult;
import com.nbcio.common.mybatis.core.page.PageQuery;
import com.nbcio.common.mybatis.core.page.TableDataInfo;
import com.nbcio.pm.domain.PmProjectDocument;
import com.nbcio.pm.domain.bo.PmProjectDocumentBo;
import com.nbcio.pm.domain.vo.PmProjectDocumentVo;
import com.nbcio.pm.mapper.PmProjectDocumentMapper;
import com.nbcio.pm.service.IPmProjectDocumentService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 项目文档Service业务层处理
 *
 * @author nbacheng
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class PmProjectDocumentServiceImpl implements IPmProjectDocumentService {

    private final PmProjectDocumentMapper baseMapper;

    @Override
    public PmProjectDocumentVo queryById(Long docId) {
        return baseMapper.selectVoById(docId);
    }

    @Override
    public TableDataInfo<PmProjectDocumentVo> queryPageList(PmProjectDocumentBo bo, PageQuery pageQuery) {
        // 使用带关联查询的方法获取所有数据
        List<PmProjectDocumentVo> allList = baseMapper.selectVoListWithJoin(
            bo.getProjectId(),
            bo.getTaskId(),
            bo.getParentId(),
            bo.getDocType(),
            bo.getDocName(),
            bo.getIsPublic()
        );
        
        // 构建树形结构
        List<PmProjectDocumentVo> treeList = buildDocumentTree(allList);
        
        
        // 对于树形结构，我们返回所有数据，不进行分页
        // 因为分页会破坏树形结构的完整性
        TableDataInfo<PmProjectDocumentVo> rspData = new TableDataInfo<>();
        rspData.setCode(200);
        rspData.setMsg("查询成功");
        rspData.setRows(treeList);
        rspData.setTotal(treeList.size());
        return rspData;
    }

    @Override
    public List<PmProjectDocumentVo> queryList(PmProjectDocumentBo bo) {
        LambdaQueryWrapper<PmProjectDocument> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    @Override
    public List<PmProjectDocumentVo> queryDocumentTree(Long projectId, Long parentId) {
        return baseMapper.selectDocumentTree(projectId, parentId);
    }

    private LambdaQueryWrapper<PmProjectDocument> buildQueryWrapper(PmProjectDocumentBo bo) {
        LambdaQueryWrapper<PmProjectDocument> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getProjectId() != null, PmProjectDocument::getProjectId, bo.getProjectId());
        lqw.eq(bo.getTaskId() != null, PmProjectDocument::getTaskId, bo.getTaskId());
        lqw.eq(bo.getParentId() != null, PmProjectDocument::getParentId, bo.getParentId());
        lqw.eq(StringUtils.isNotBlank(bo.getDocType()), PmProjectDocument::getDocType, bo.getDocType());
        lqw.like(StringUtils.isNotBlank(bo.getDocName()), PmProjectDocument::getDocName, bo.getDocName());
        lqw.eq(bo.getIsPublic() != null, PmProjectDocument::getIsPublic, bo.getIsPublic());
        lqw.orderByAsc(PmProjectDocument::getSortOrder);
        lqw.orderByDesc(PmProjectDocument::getCreateTime);
        return lqw;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(PmProjectDocumentBo bo) {
        PmProjectDocument add = MapstructUtils.convert(bo, PmProjectDocument.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setDocId(add.getDocId());
        }
        return flag;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByBo(PmProjectDocumentBo bo) {
        PmProjectDocument update = MapstructUtils.convert(bo, PmProjectDocument.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 构建文档树形结构
     */
    private List<PmProjectDocumentVo> buildDocumentTree(List<PmProjectDocumentVo> documents) {
        Map<Long, PmProjectDocumentVo> map = new HashMap<>();
        List<PmProjectDocumentVo> roots = new ArrayList<>();
        
        // 创建映射，初始化children数组
        for (PmProjectDocumentVo doc : documents) {
            doc.setChildrenVo(new ArrayList<>());
            map.put(doc.getDocId(), doc);
        }
        
        // 构建树形结构
        for (PmProjectDocumentVo doc : documents) {
            if (doc.getParentId() != null && doc.getParentId() != 0 && map.containsKey(doc.getParentId())) {
                // 有父节点，添加到父节点的children中
                map.get(doc.getParentId()).getChildrenVo().add(doc);
            } else {
                // 没有父节点，作为根节点
                roots.add(doc);
            }
        }
        
        // 为文件夹设置hasChildren属性
        setHasChildren(roots);
        return roots;
    }
    
    /**
     * 设置hasChildren属性
     */
    private void setHasChildren(List<PmProjectDocumentVo> docs) {
        for (PmProjectDocumentVo doc : docs) {
            if ("folder".equals(doc.getDocType())) {
                boolean hasChildren = doc.getChildrenVo() != null && !doc.getChildrenVo().isEmpty();
                doc.setHasChildren(hasChildren);
                if (hasChildren) {
                    setHasChildren(doc.getChildrenVo());
                }
            } else {
                // 文件不能有子项
                doc.setHasChildren(false);
                doc.setChildrenVo(new ArrayList<>());
            }
        }
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(PmProjectDocument entity) {
        // TODO 做一些数据校验,如唯一约束
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PmProjectDocumentVo uploadFile(MultipartFile file, Long projectId, Long taskId, Long parentId, String docName, Boolean isPublic) {
        try {
            // 获取文件信息
            String originalFilename = file.getOriginalFilename();
            String fileName = StringUtils.isNotBlank(docName) ? docName : originalFilename;
            String fileExtension = getFileExtension(originalFilename);
            long fileSize = file.getSize();
            
            // 根据配置选择存储方式
            String filePath = uploadFileToStorage(file, fileExtension);
            
            // 创建文档记录
            PmProjectDocumentBo bo = new PmProjectDocumentBo();
            bo.setProjectId(projectId);
            bo.setTaskId(taskId);
            bo.setParentId(parentId);
            bo.setDocName(fileName);
            bo.setDocType("file");
            bo.setFilePath(filePath);
            bo.setFileSize(fileSize);
            bo.setFileExtension(fileExtension);
            bo.setVersion("1.0");
            bo.setIsPublic(isPublic != null ? isPublic : false);
            bo.setDownloadCount(0);
            
            // 保存到数据库
            insertByBo(bo);
            
            return queryById(bo.getDocId());
        } catch (com.nbcio.common.core.exception.file.InvalidExtensionException e) {
            log.error("文件类型不支持", e);
            throw new RuntimeException("文件类型不支持: " + e.getMessage());
        } catch (Exception e) {
            log.error("文件上传失败", e);
            throw new RuntimeException("文件上传失败: " + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PmProjectDocumentVo createFolder(Long projectId, Long taskId, Long parentId, String folderName, Boolean isPublic) {
        PmProjectDocumentBo bo = new PmProjectDocumentBo();
        bo.setProjectId(projectId);
        bo.setTaskId(taskId);
        bo.setParentId(parentId);
        bo.setDocName(folderName);
        bo.setDocType("folder");
        bo.setIsPublic(isPublic != null ? isPublic : false);
        bo.setDownloadCount(0);
        
        insertByBo(bo);
        return queryById(bo.getDocId());
    }

    @Override
    public void downloadFile(Long docId) {
        PmProjectDocumentVo document = queryById(docId);
        if (document == null) {
            throw new RuntimeException("文档不存在");
        }
        
        try {
            // 更新下载次数
            updateDownloadCount(docId);
            
            // 根据文件路径获取文件
            File file = getFileFromStorage(document.getFilePath());
            if (!file.exists()) {
                throw new RuntimeException("文件不存在");
            }
            
            // 注意：实际的文件下载处理应该在Controller层完成
            // 这里只是验证文件存在性和更新下载次数
            log.info("文件下载请求: docId={}, fileName={}", docId, document.getDocName());
        } catch (Exception e) {
            log.error("文件下载失败", e);
            throw new RuntimeException("文件下载失败: " + e.getMessage());
        }
    }

    @Override
    public Boolean updateDownloadCount(Long docId) {
        return baseMapper.updateDownloadCount(docId) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean moveDocument(Long docId, Long newParentId) {
        PmProjectDocument document = baseMapper.selectById(docId);
        if (document == null) {
            return false;
        }
        
        document.setParentId(newParentId);
        return baseMapper.updateById(document) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PmProjectDocumentVo copyDocument(Long docId, Long newParentId) {
        PmProjectDocumentVo original = queryById(docId);
        if (original == null) {
            throw new RuntimeException("原文档不存在");
        }
        
        PmProjectDocumentBo bo = new PmProjectDocumentBo();
        bo.setProjectId(original.getProjectId());
        bo.setTaskId(original.getTaskId());
        bo.setParentId(newParentId);
        bo.setDocName(original.getDocName() + "_副本");
        bo.setDocType(original.getDocType());
        bo.setFilePath(original.getFilePath());
        bo.setFileSize(original.getFileSize());
        bo.setFileExtension(original.getFileExtension());
        bo.setVersion(original.getVersion());
        bo.setIsPublic(original.getIsPublic());
        bo.setDownloadCount(0);
        
        insertByBo(bo);
        return queryById(bo.getDocId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean renameDocument(Long docId, String newName) {
        PmProjectDocument document = baseMapper.selectById(docId);
        if (document == null) {
            return false;
        }
        
        document.setDocName(newName);
        return baseMapper.updateById(document) > 0;
    }

    @Override
    public String getDocumentPath(Long docId) {
        PmProjectDocumentVo document = queryById(docId);
        if (document == null) {
            return null;
        }
        
        StringBuilder path = new StringBuilder(document.getDocName());
        Long parentId = document.getParentId();
        
        while (parentId != null && parentId != 0) {
            PmProjectDocumentVo parent = queryById(parentId);
            if (parent == null) {
                break;
            }
            path.insert(0, parent.getDocName() + "/");
            parentId = parent.getParentId();
        }
        
        return path.toString();
    }

    /**
     * 根据配置上传文件到存储系统
     */
    private String uploadFileToStorage(MultipartFile file, String fileExtension) throws IOException, com.nbcio.common.core.exception.file.InvalidExtensionException {
        String uploadType = com.nbcio.common.core.config.RuoYiConfig.getUploadtype();
        
        if ("local".equals(uploadType)) {
            // 本地存储
            String profile = com.nbcio.common.core.config.RuoYiConfig.getProfile();
            
            // 临时修复：如果profile路径末尾有'e'，去掉它
            if (profile.endsWith("/e")) {
                profile = profile.substring(0, profile.length() - 2);
            } else if (profile.endsWith("e")) {
                profile = profile.substring(0, profile.length() - 1);
            }
            
            // 使用修复后的profile重新计算uploadPath
            String uploadPath = profile + "/upload";
            
            String filePath = FileUploadUtils.upload(uploadPath, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
            
            // 验证文件是否真的存在
            // FileUploadUtils.upload返回的路径已经包含了/profile前缀，需要去掉
            String actualFilePath = filePath;
            if (filePath.startsWith("/profile")) {
                actualFilePath = filePath.substring(8); // 去掉 /profile 前缀（8个字符）
            }
            
            String fullPath = profile + actualFilePath;
            java.io.File uploadedFile = new java.io.File(fullPath);
            if (!uploadedFile.exists()) {
                log.warn("上传文件不存在！完整路径: '{}'", fullPath);
            }
            
            return filePath;
        } else if ("minio".equals(uploadType) || "alioss".equals(uploadType)) {
            // OSS存储
            OssClient ossClient = OssFactory.instance();
            UploadResult uploadResult = ossClient.uploadSuffix(file.getInputStream(), "." + fileExtension, file.getContentType());
            return uploadResult.getUrl();
        } else {
            throw new RuntimeException("不支持的存储类型: " + uploadType);
        }
    }

    /**
     * 从存储系统获取文件
     */
    private File getFileFromStorage(String filePath) {
        String uploadType = com.nbcio.common.core.config.RuoYiConfig.getUploadtype();
        
        if ("local".equals(uploadType)) {
            // 本地存储
            String profile = com.nbcio.common.core.config.RuoYiConfig.getProfile();
            return new File(profile + filePath);
        } else {
            // OSS存储需要特殊处理，这里简化处理
            throw new RuntimeException("OSS存储的文件下载需要特殊处理");
        }
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        if (StringUtils.isBlank(filename)) {
            return "";
        }
        int lastDotIndex = filename.lastIndexOf('.');
        if (lastDotIndex == -1) {
            return "";
        }
        return filename.substring(lastDotIndex + 1).toLowerCase();
    }
}
